{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Chapter 6 - Evolutionary Algorithms\n",
    "### Deep Reinforcement Learning *in Action*"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### Listing 6.1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "import random\n",
    "import numpy as np\n",
    "from matplotlib import pyplot as plt\n",
    "\n",
    "alphabet = \"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,.! \" #A\n",
    "target = \"Hello World!\" #B\n",
    "\n",
    "class Individual: #C\n",
    "    def __init__(self, string, fitness=0):\n",
    "        self.string = string\n",
    "        self.fitness = fitness\n",
    "\n",
    "from difflib import SequenceMatcher\n",
    "\n",
    "def similar(a, b): #D\n",
    "    return SequenceMatcher(None, a, b).ratio()\n",
    "\n",
    "def spawn_population(length=26,size=100): #E\n",
    "    pop = []\n",
    "    for i in range(size):\n",
    "        string = ''.join(random.choices(alphabet,k=length))\n",
    "        individual = Individual(string)\n",
    "        pop.append(individual)\n",
    "    return pop\n",
    "\n",
    "#A This is the list of characters we sample from to produce random strings\n",
    "#B This is the string we’re trying to evolve from a random population\n",
    "#C We set up a simple class to store information about each member of the population\n",
    "#D This method will compute a similarity metric between two strings, giving us a fitness score\n",
    "#E This method will produce an initial random population of strings"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### Listing 6.2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "def recombine(p1_, p2_): #A\n",
    "    p1 = p1_.string\n",
    "    p2 = p2_.string\n",
    "    child1 = []\n",
    "    child2 = []\n",
    "    cross_pt = random.randint(0,len(p1))\n",
    "    child1.extend(p1[0:cross_pt])\n",
    "    child1.extend(p2[cross_pt:])\n",
    "    child2.extend(p2[0:cross_pt])\n",
    "    child2.extend(p1[cross_pt:])\n",
    "    c1 = Individual(''.join(child1))\n",
    "    c2 = Individual(''.join(child2))\n",
    "    return c1, c2\n",
    "\n",
    "def mutate(x, mut_rate=0.01): #B\n",
    "    new_x_ = []\n",
    "    for char in x.string:\n",
    "        if random.random() < mut_rate:\n",
    "            new_x_.extend(random.choices(alphabet,k=1))\n",
    "        else:\n",
    "            new_x_.append(char)\n",
    "    new_x = Individual(''.join(new_x_))\n",
    "    return new_x\n",
    "\n",
    "#A This function recombines two parent strings into two new offspring\n",
    "#B This function will mutate a string by randomly flipping characters"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### Listing 6.3"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 186,
   "metadata": {},
   "outputs": [],
   "source": [
    "def evaluate_population(pop, target): #A\n",
    "    avg_fit = 0\n",
    "    for i in range(len(pop)):\n",
    "        fit = similar(pop[i].string, target)\n",
    "        pop[i].fitness = fit\n",
    "        avg_fit += fit\n",
    "    avg_fit /= len(pop)\n",
    "    return pop, avg_fit\n",
    "\n",
    "def next_generation(pop, size=100, length=26, mut_rate=0.01): #B\n",
    "    new_pop = []\n",
    "    while len(new_pop) < size:\n",
    "        parents = random.choices(pop,k=2, weights=[x.fitness for x in pop])\n",
    "        offspring_ = recombine(parents[0],parents[1])\n",
    "        child1 = mutate(offspring_[0], mut_rate=mut_rate)\n",
    "        child2 = mutate(offspring_[1], mut_rate=mut_rate)\n",
    "        offspring = [child1, child2]\n",
    "        new_pop.extend(offspring)\n",
    "    return new_pop\n",
    "\n",
    "#A This function assigns a fitness score to each individual in the population\n",
    "#B This function generates a new generation by recombination and mutation"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### Listing 6.4"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 203,
   "metadata": {},
   "outputs": [],
   "source": [
    "num_generations = 150\n",
    "population_size = 900\n",
    "str_len = len(target)\n",
    "mutation_rate = 0.00001 #A\n",
    "\n",
    "pop_fit = []\n",
    "pop = spawn_population(size=population_size, length=str_len) #B\n",
    "done = False\n",
    "for gen in range(num_generations):\n",
    "    pop, avg_fit = evaluate_population(pop, target)\n",
    "    pop_fit.append(avg_fit) #C\n",
    "    new_pop = next_generation(pop, \\\n",
    "        size=population_size, length=str_len, mut_rate=mutation_rate)\n",
    "    pop = new_pop\n",
    "    for x in pop: \n",
    "        if x.string == target: \n",
    "            print(\"Target Found!\")\n",
    "            done = True\n",
    "    if done:\n",
    "        break\n",
    "#A Set the mutation rate to 0.1%\n",
    "#B Create the initial random population\n",
    "#C Record population average fitness over training time"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 204,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'QHelolo Tor!'"
      ]
     },
     "execution_count": 204,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "pop.sort(key=lambda x: x.fitness, reverse=True) #sort in place, highest fitness first\n",
    "pop[0].string"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 207,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 864x504 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.figure(figsize=(12,7))\n",
    "plt.xlabel(\"Generations\",fontsize=22)\n",
    "plt.ylabel(\"Fitness\",fontsize=22)\n",
    "plt.plot(pop_fit)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### Listing 6.5\n",
    "CartPole Example"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "import torch"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Total number of parameters: $4*25 + 25 + 10*25 + 10 + 2*10 + 2 = 407$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 872,
   "metadata": {},
   "outputs": [],
   "source": [
    "def model(x,unpacked_params):\n",
    "    l1,b1,l2,b2,l3,b3 = unpacked_params #A\n",
    "    y = torch.nn.functional.linear(x,l1,b1) #B\n",
    "    y = torch.relu(y) #C\n",
    "    y = torch.nn.functional.linear(y,l2,b2)\n",
    "    y = torch.relu(y)\n",
    "    y = torch.nn.functional.linear(y,l3,b3)\n",
    "    y = torch.log_softmax(y,dim=0) #D\n",
    "    return y\n",
    "\n",
    "#A Unpack the parameter vector into individual layer matrices\n",
    "#B Simple linear layer with bias\n",
    "#C Rectified linear unit activation function\n",
    "#D The last layer will output log-probabilities over actions"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### Listing 6.6"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 873,
   "metadata": {},
   "outputs": [],
   "source": [
    "def unpack_params(params, layers=[(25,4),(10,25),(2,10)]): #A\n",
    "    unpacked_params = [] #B\n",
    "    e = 0\n",
    "    for i,l in enumerate(layers): #C\n",
    "        s,e = e,e+np.prod(l)\n",
    "        weights = params[s:e].view(l) #D\n",
    "        s,e = e,e+l[0]\n",
    "        bias = params[s:e]\n",
    "        unpacked_params.extend([weights,bias]) #E\n",
    "    return unpacked_params\n",
    "\n",
    "#A The `layers` parameter specifies the shape of each layer matrix\n",
    "#B Store each individual layer tensor\n",
    "#C Iterate through each layer\n",
    "#D Unpack the indivudal layer into matrix form\n",
    "#E Add the unpacked tensor to the list"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### Listing 6.7"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 874,
   "metadata": {},
   "outputs": [],
   "source": [
    "def spawn_population(N=50,size=407): #A\n",
    "    pop = []\n",
    "    for i in range(N):\n",
    "        vec = torch.randn(size) / 2.0 #B\n",
    "        fit = 0\n",
    "        p = {'params':vec, 'fitness':fit} #C\n",
    "        pop.append(p)\n",
    "    return pop\n",
    "\n",
    "#A `N` is the number of individuals in the population, `size` is the length of the parameter vectors\n",
    "#B Create a randomly initialized parameter vector\n",
    "#C Create a dictionary to store the parameter vector and its associated fitness score"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### Listing 6.8"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 875,
   "metadata": {},
   "outputs": [],
   "source": [
    "def recombine(x1,x2): #A\n",
    "    x1 = x1['params'] #B\n",
    "    x2 = x2['params']\n",
    "    l = x1.shape[0]\n",
    "    split_pt = np.random.randint(l) #C\n",
    "    child1 = torch.zeros(l)\n",
    "    child2 = torch.zeros(l)\n",
    "    child1[0:split_pt] = x1[0:split_pt] #D\n",
    "    child1[split_pt:] = x2[split_pt:]\n",
    "    child2[0:split_pt] = x2[0:split_pt]\n",
    "    child2[split_pt:] = x1[split_pt:]\n",
    "    c1 = {'params':child1, 'fitness': 0.0} #E\n",
    "    c2 = {'params':child2, 'fitness': 0.0}\n",
    "    return c1, c2\n",
    "\n",
    "#A x1 and x2 are agents which are dictionaries\n",
    "#B Extract just the parameter vector\n",
    "#C Randomly produce a split or crossover point\n",
    "#D The first child is produced by taking the first segment of parent 1 and the second segment of parent 2\n",
    "#E Create new children agents by packaging the new parameter vectors into dictionaries"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### Listing 6.9"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 876,
   "metadata": {},
   "outputs": [],
   "source": [
    "def mutate(x, rate=0.01): #A\n",
    "    x_ = x['params']\n",
    "    num_to_change = int(rate * x_.shape[0]) #B\n",
    "    idx = np.random.randint(low=0,high=x_.shape[0],size=(num_to_change,))\n",
    "    x_[idx] = torch.randn(num_to_change) / 10.0 #C\n",
    "    x['params'] = x_\n",
    "    return x\n",
    "\n",
    "#A `rate` is the mutation rate where 0.01 is a 1% mutation rate\n",
    "#B Use the mutation rate to decide how many elements in the parameter vector to mutate\n",
    "#C Randomly reset the selected elements in the parameter vector"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### Listing 6.10"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 877,
   "metadata": {},
   "outputs": [],
   "source": [
    "import gym\n",
    "env = gym.make(\"CartPole-v0\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 878,
   "metadata": {},
   "outputs": [],
   "source": [
    "def test_model(agent):\n",
    "    done = False\n",
    "    state = torch.from_numpy(env.reset()).float()\n",
    "    score = 0\n",
    "    while not done: #A\n",
    "        params = unpack_params(agent['params'])\n",
    "        probs = model(state,params) #B\n",
    "        action = torch.distributions.Categorical(probs=probs).sample() #C\n",
    "        state_, reward, done, info = env.step(action.item())\n",
    "        state = torch.from_numpy(state_).float()\n",
    "        score += 1 #D\n",
    "    return score\n",
    "\n",
    "#A While game is not lost\n",
    "#B Get the action probabilities from the model using the agent's parameter vector\n",
    "#C Probabilistically select an action by sampling from a categorical distribution\n",
    "#D Keep track of the number of time steps the game is not lost as the score"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### Listing 6.11"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 879,
   "metadata": {},
   "outputs": [],
   "source": [
    "def evaluate_population(pop):\n",
    "    tot_fit = 0 #A\n",
    "    lp = len(pop)\n",
    "    for agent in pop: #B\n",
    "        score = test_model(agent) #C\n",
    "        agent['fitness'] = score #D\n",
    "        tot_fit += score\n",
    "    avg_fit = tot_fit / lp\n",
    "    return pop, avg_fit\n",
    "\n",
    "#A Total fitness for this population; used to later calculate the average fitness of the population\n",
    "#B Iterate through each agent in the population\n",
    "#C Run the agent in the environment to assess its fitness\n",
    "#D Store the fitness value"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### Listing 6.12"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 883,
   "metadata": {},
   "outputs": [],
   "source": [
    "def next_generation(pop,mut_rate=0.001,tournament_size=0.2):\n",
    "    new_pop = []\n",
    "    lp = len(pop)\n",
    "    while len(new_pop) < len(pop): #A\n",
    "        rids = np.random.randint(low=0,high=lp,size=(int(tournament_size*lp))) #B\n",
    "        batch = np.array([[i,x['fitness']] for (i,x) in enumerate(pop) if i in rids]) #C\n",
    "        scores = batch[batch[:, 1].argsort()] #D\n",
    "        i0, i1 = int(scores[-1][0]),int(scores[-2][0]) #E\n",
    "        parent0,parent1 = pop[i0],pop[i1]\n",
    "        offspring_ = recombine(parent0,parent1) #F\n",
    "        child1 = mutate(offspring_[0], rate=mut_rate) #G\n",
    "        child2 = mutate(offspring_[1], rate=mut_rate)\n",
    "        offspring = [child1, child2]\n",
    "        new_pop.extend(offspring)\n",
    "    return new_pop\n",
    "\n",
    "#A While the new population is not full\n",
    "#B Select a percentage of the full population as a subset\n",
    "#C Subset the population to get a batch of agents and match each one with their index value in the original population\n",
    "#D Sort this batch in increasing order of score\n",
    "#E The last agents in the sorted batch are the agents with the highest scores; select the top 2 as parents\n",
    "#F Recombine the parents to get offspring\n",
    "#G Mutate the children before putting them into the next generation"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### Listing 6.13"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 912,
   "metadata": {},
   "outputs": [],
   "source": [
    "num_generations = 20 #A\n",
    "population_size = 500 #B\n",
    "mutation_rate = 0.01\n",
    "pop_fit = []\n",
    "pop = spawn_population(N=population_size,size=407) #C\n",
    "for i in range(num_generations):\n",
    "    pop, avg_fit = evaluate_population(pop) #D\n",
    "    pop_fit.append(avg_fit)\n",
    "    pop = next_generation(pop, mut_rate=mutation_rate,tournament_size=0.2) #E\n",
    "    \n",
    "#A The number of generations to evolve\n",
    "#B The number of individuals in each generation\n",
    "#C Initialize a population\n",
    "#D Evaluate the fitness of each agent in the population\n",
    "#E Populate the next generation"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 913,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[<matplotlib.lines.Line2D at 0x12138ccc0>]"
      ]
     },
     "execution_count": 913,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 864x504 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.figure(figsize=(12,7))\n",
    "plt.xlabel(\"Generations\",fontsize=22)\n",
    "plt.ylabel(\"Score\",fontsize=22)\n",
    "plt.plot(running_mean(np.array(pop_fit),3))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 924,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "200"
      ]
     },
     "execution_count": 924,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "test_model(pop[0])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 939,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/Users/brandonbrown/anaconda3/envs/deeprl/lib/python3.6/site-packages/ipykernel/__main__.py:6: RuntimeWarning: divide by zero encountered in log\n"
     ]
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 1080x504 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.figure(figsize=(15,7))\n",
    "plt.axis('off')\n",
    "plt.xlabel(\"Compute resources\",fontsize=22)\n",
    "plt.ylabel(\"Performance\",fontsize=22)\n",
    "x = np.linspace(0,100,1000)\n",
    "y = np.log(x)\n",
    "plt.plot(x,y)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 204,
   "metadata": {},
   "outputs": [],
   "source": [
    "def running_mean(x,n=5):\n",
    "    conv = np.ones(n)\n",
    "    y = np.zeros(x.shape[0]-n)\n",
    "    for i in range(x.shape[0]-n):\n",
    "        y[i] = (conv @ x[i:i+n]) / n\n",
    "    return y"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python [conda env:deeprl]",
   "language": "python",
   "name": "conda-env-deeprl-py"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.7"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
